﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace LFM_Playlist_Builder
{
    public partial class Form1 : Form
    {
        String inPath = "";
        String outPath = "";
        int theExtension = Global.PLAYLISTFORMATDEFAULT;

        public Form1()
        {
            InitializeComponent();
            checkUsername();
            openFD.DefaultExt = "m3u";
            openFD.Filter = "Playlist Files|*.m3u|All Files|*.*";
            saveFD.DefaultExt = "m3u8";
            saveFD.Filter = "Unicode Playlist Files|*.m3u8|Playlist Files|*.m3u|All Files|*.*";

            chkLoved.Checked = Properties.Settings.Default.chkLoved;
            chkRecommended.Checked = Properties.Settings.Default.chkRec;
            chkLovedArtists.Checked = Properties.Settings.Default.chkLovedA;
            chkLovedT.Checked = Properties.Settings.Default.chkSimT;
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            rtOutput.Text = "";
            if (Properties.Settings.Default.username != "" && Properties.Settings.Default.username != null)
            {
                switch(Properties.Settings.Default.loadChoice)
                {
                    case Global.LOADLIBRARY:
                        //Remove extension from the library path for the nde wrapper
                        inPath = Properties.Settings.Default.libraryFilePath;
                        inPath = inPath.Remove(inPath.Length-4);
                        break;

                    case Global.LOADPLAYLIST:
                        inPath = Properties.Settings.Default.playlistFilePath;
                        break;

                    case Global.LOADCHOOSE:
                        break;

                    default:
                        break;
                }
                if (inPath == null || inPath == "")
                {
                    if (openFD.ShowDialog() == DialogResult.OK)
                    {
                        inPath = openFD.FileName;
                    }
                }

                if (inPath != null && inPath != "")
                {
                    if (saveFD.ShowDialog() == DialogResult.OK)
                    {
                        //If "OK" is pressed
                        outPath = saveFD.FileName;
                        enableComponents(false);
                        theExtension = checkExtension(outPath);
                        bckWorker.RunWorkerAsync();
                    }
                }

            }
            else
            {
                rtOutput.Text = "Please enter username in the 'Settings' menu";
            }
        }

        private int getLowerValue(int a, int b)
        {
            if (a <= b)
            {
                return a;
            }
            else
            {
                return b;
            }
        }

        private void bckWorker_DoWork(object sender, DoWorkEventArgs e) 
        {
            Boolean loadError = false;
            try
            {
                List<TrackInfo> playlist = new List<TrackInfo>();
                List<TrackInfo> tempLast = new List<TrackInfo>();
                LastFMXML lastInit = new LastFMXML(Properties.Settings.Default.username, getLowerValue(Properties.Settings.Default.fetchSize, Global.MAXTRACKNUMBER), 0);

                WmlReader wmlInit = new WmlReader(inPath, (float)Properties.Settings.Default.similarity);

                int fetchSize = 0;

                HashSet<String> artistsHash = new HashSet<String>();
                HashSet<String> recArtistsHash = new HashSet<String>();

                Boolean loved = chkLoved.Checked;
                Boolean recommended = chkRecommended.Checked;
                Boolean lovedA = chkLovedArtists.Checked;
                Boolean sortPlayCount = false;

                while ((loved || recommended || lovedA) && loadError == false) //Allows us to append tracklist with multiple load types
                {

                    if (loved)
                    {
                        lastInit = new LastFMXML(Properties.Settings.Default.username, getLowerValue(Properties.Settings.Default.fetchSize, Global.MAXTRACKNUMBER), 0);
                        this.Invoke((MethodInvoker)delegate
                        {
                            rtOutput.Text = (rtOutput.Text + "Loading " + Properties.Settings.Default.username +  "'s loved tracks\n");
                            loved = false;
                            fetchSize = Properties.Settings.Default.fetchSize;
                        });
                        if (Properties.Settings.Default.sortPlaycount == true)
                        {
                            sortPlayCount = true;
                        }
                    }
                    else if (lovedA)
                    {
                        this.Invoke((MethodInvoker)delegate
                        {
                            foreach (TrackInfo theTrack in tempLast)
                            {
                                artistsHash.Add(theTrack.getArtist());
                            }
                            lastInit = new LastFMXML(Properties.Settings.Default.username, getLowerValue(Properties.Settings.Default.lovedAFetchSize, Global.MAXTRACKNUMBER), 2, artistsHash);
                            rtOutput.Text = (rtOutput.Text + "Loading " + Properties.Settings.Default.lovedAFetchSize + " tracks of each of " + Properties.Settings.Default.username
                                + "'s loved artists\n");
                            lovedA = false;
                            fetchSize = Properties.Settings.Default.lovedAFetchSize;
                        });
                    }
                    else if (recommended)
                    {
                        lastInit = new LastFMXML(Properties.Settings.Default.username, getLowerValue(Properties.Settings.Default.recFetchSize, Global.MAXTRACKNUMBER), 1);
                        this.Invoke((MethodInvoker)delegate
                        {
                            rtOutput.Text = (rtOutput.Text + "Loading " + Properties.Settings.Default.username + "'s recommended tracks\n");
                            recommended = false;
                            fetchSize = Properties.Settings.Default.recFetchSize;
                        });
                    }

                    int attemptSize = Global.MAXTRACKNUMBER;

                    if (fetchSize < Global.MAXTRACKNUMBER)
                    {
                        attemptSize = fetchSize;
                    }


                    this.Invoke((MethodInvoker)delegate
                    {
                        prgBar.Value = 0;
                    });

                    int tracksDone = 0;

                    while (tracksDone < fetchSize && loadError == false)
                    {
                        this.Invoke((MethodInvoker)delegate
                        {
                            rtOutput.Text = (rtOutput.Text + "Loading XML for tracks " + tracksDone + " to " + (tracksDone + attemptSize) + ". This may take a while.\n");
                            if (prgBar.Value < 45)
                            {
                                prgBar.Value += 5;
                            }
                        });

                        int loadStatus = 0;

                        while ((loadStatus = lastInit.load((tracksDone / attemptSize) + 1)) == Global.LOADERROR && loadError == false)
                        {
                            this.Invoke((MethodInvoker)delegate
                            {
                                if (prgBar.Value < 45)
                                {
                                    prgBar.Value += 5;
                                }
                            });

                            if (attemptSize > Global.MINTRACKNUMBER)
                            {
                                this.Invoke((MethodInvoker)delegate
                                {
                                    rtOutput.Text = (rtOutput.Text + "Loading XML from profile failed at " + attemptSize + " tracks, trying at " + (attemptSize = attemptSize / 2) + "\n");
                                });
                                lastInit.setTrackCount(attemptSize);
                            }
                            else
                            {
                                this.Invoke((MethodInvoker)delegate
                                {
                                    loadError = true;
                                    rtOutput.Text = (rtOutput.Text + "Loading XML from profile timed out, please check network connection\n");
                                });
                            }

                        }

                        if (loadStatus == Global.MISCERROR)
                        {
                            this.Invoke((MethodInvoker)delegate
                            {
                                loadError = true;
                                rtOutput.Text = (rtOutput.Text + "Loading XML from profile failed due to an unknown error\n");
                            });
                        }
                        else if (loadStatus == Global.AUTHERROR) //If there was an auth error
                        {
                            this.Invoke((MethodInvoker)delegate
                            {
                                loadError = true;
                                rtOutput.Text = (rtOutput.Text + "Error Authenticating\n");
                            });
                        }

                        tempLast.AddRange(lastInit.getTrackInfo((tracksDone / attemptSize) + 1));
                        tracksDone += attemptSize;
                    }

                    if (sortPlayCount == true)
                    {
                        this.Invoke((MethodInvoker)delegate
                        {
                            rtOutput.Text = (rtOutput.Text + "Fetching playcount\n");
                        });
                        tempLast = lastInit.setPlayCounts(tempLast);
                    }
                }



                if (!loadError) //We don't want to do this if there was a load error!
                {
                    this.Invoke((MethodInvoker)delegate
                    {
                        rtOutput.Text = (rtOutput.Text + "Sorting Lastfm Tracks\n");
                        prgBar.Value = 45;
                    });

                    tempLast = lastInit.setIndexes(tempLast);
                    tempLast = wmlInit.alphabetizePlaylist(tempLast);

                    this.Invoke((MethodInvoker)delegate
                    {
                        rtOutput.Text = (rtOutput.Text + "Retrieving tracks\n");
                        prgBar.Value = 50;
                    });

                    switch(Properties.Settings.Default.loadChoice)
                    {
                        case Global.LOADLIBRARY:
                            wmlInit.readWinampLibrary();
                            break;

                        case Global.LOADPLAYLIST:
                            wmlInit.readLibrary();
                            break;

                        case Global.LOADCHOOSE:
                            wmlInit.readLibrary();
                            break;

                        default:
                            wmlInit.readLibrary();
                            break;
                    }



                    /*
                    //For debug only - checks to see what's coming in from the last.fm XML
                    foreach (String i in lastInit.getTracks())
                    {
                        this.Invoke((MethodInvoker)delegate
                        {
                            rtOutput.Text = (rtOutput.Text + i + "\n");
                        });
                    }
                     */

                    //Let's not do this for now as we are not finding out similarity of both artists and titles at the same time.
                    /*
                    this.Invoke((MethodInvoker)delegate
                    {
                        rtOutput.Text = (rtOutput.Text + "Sorting Library\n");
                        prgBar.Value = 60;
                    });

                    wmlInit.alphabetizeThis();
                     */

                    //For the user's info
                    this.Invoke((MethodInvoker)delegate
                    {
                        rtOutput.Text = (rtOutput.Text + "Building playlist\n");
                        prgBar.Value = 75;

                        int corrected = 0;
                        int incorrect = 0;

                        playlist = wmlInit.buildPlaylist(tempLast);
                        if (Properties.Settings.Default.songNames == true)
                        {
                            rtOutput.Text = (rtOutput.Text + "Searching for similar tracks\n");
                            playlist = wmlInit.buildPlaylistSimilar(playlist, Properties.Settings.Default.artistNames == true);
                        }

                        this.Invoke((MethodInvoker)delegate
                        {
                            rtOutput.Text = (rtOutput.Text + "Fixing Playlist order\n");
                            prgBar.Value = 80;
                        });

                        playlist = wmlInit.revertPlaylist(playlist);
                        if (Properties.Settings.Default.sortPlaycount == true)
                        {
                            playlist = wmlInit.sortPlaylistByPlayCount(playlist);
                        }

                        this.Invoke((MethodInvoker)delegate
                        {
                            rtOutput.Text = (rtOutput.Text + "Removing Duplicates\n");
                            prgBar.Value = 85;
                        });

                        playlist = wmlInit.removePlaylistDuplicates(playlist);

                        String tempOutput = "";

                        int lastMode = 0;
                        int subScore = 0;

                        if (Properties.Settings.Default.showCorrections == true)
                        {
                            tempOutput = (tempOutput + "Corrected the following tracks:\n---\n");
                            foreach (TrackInfo i in (playlist))
                            {
                                if (lastMode != i.getMode())
                                {

                                    lastMode = i.getMode();
                                    tempOutput = (tempOutput + getModeText(lastMode, corrected - subScore));
                                    subScore = corrected;
                                }

                                if (i.isFound())
                                {
                                    if (i.getScore() > (float)0.1)
                                    {
                                        tempOutput = (tempOutput + i.getArtist() + " - " + i.getTrackName() + ".\n Closest match is " + i.getFileName() + "\n---\n");
                                        corrected++;
                                    }
                                }
                            }

                            tempOutput = (tempOutput + getModeText(Global.MNONE, corrected - subScore));
                        }

                        lastMode = 0;
                        subScore = 0;

                        if (Properties.Settings.Default.showNotFound == true)
                        {
                            tempOutput = (tempOutput + "Could not find the following tracks!\n---\n");
                            foreach (TrackInfo i in (playlist))
                            {
                                if (lastMode != i.getMode())
                                {
                                    lastMode = i.getMode();
                                    tempOutput = (tempOutput + getModeText(lastMode, corrected - subScore));
                                    subScore = corrected;
                                }

                                if (i.getMode() == Global.MREC)
                                {
                                    recArtistsHash.Add(i.getArtist());
                                }

                                if (!i.isFound())
                                {
                                    tempOutput = (tempOutput + i.getArtist() + " - " + i.getTrackName() + "\n---\n");
                                    incorrect++;
                                }
                            }
                            tempOutput = (tempOutput + getModeText(Global.MNONE, corrected - subScore));
                        }

                        tempOutput = (tempOutput + "Number of tracks corrected: " + corrected + "\nNumber of tracks not found: " + incorrect + "\n");

                        if (corrected > 0 || incorrect > 0)
                        {
                            rtOutput.Text = (rtOutput.Text + tempOutput);
                        }
                        else
                        {
                            rtOutput.Text = (rtOutput.Text + "All Tracks Found!\n");
                        }

                        if (Properties.Settings.Default.showLovedA == true)
                        {
                            rtOutput.Text = (rtOutput.Text + "\nThese are your loved artists:\n\n");
                            foreach (String theArist in artistsHash)
                            {
                                rtOutput.Text = (rtOutput.Text + theArist + "\n");
                            }
                            rtOutput.Text = (rtOutput.Text + "\n" + artistsHash.Count + "total artists.\n\n");
                        }
                        rtOutput.Text = (rtOutput.Text + "\nThese are your recommended artists:\n\n");
                        foreach (String theArist in recArtistsHash)
                        {
                            rtOutput.Text = (rtOutput.Text + theArist + "\n");
                        }
                        rtOutput.Text = (rtOutput.Text + "\n" + artistsHash.Count + "total artists.\n\n");

                    });

                    this.Invoke((MethodInvoker)delegate
                    {
                        if (wmlInit.exportPlaylist(playlist, outPath, theExtension))
                        {

                                rtOutput.Text = (rtOutput.Text + "Done!\n");
                                prgBar.Value = 100;

                        }
                        else
                        {
                                rtOutput.Text = (rtOutput.Text + "Saving file failed!\n");
                                prgBar.Value = 0;
                        }
                    });

                }
                else
                {
                    this.Invoke((MethodInvoker)delegate
                    {
                        prgBar.Value = 0;
                    });
                }
            }
            finally
            {
                this.Invoke((MethodInvoker)delegate
                {
                    enableComponents(true);
                });
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox1 aboutBox = new AboutBox1();
            aboutBox.Show();
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SettingsForm settingsForm1 = new SettingsForm();
            settingsForm1.ShowDialog();
            checkUsername();

        }

        private void checkUsername()
        {
            if (Properties.Settings.Default.username != "" && Properties.Settings.Default.username != null)
            {
                btnOpen.Enabled = true;
                rtOutput.Text = "";
            }
            else
            {
                btnOpen.Enabled = false;
                rtOutput.Text = "Please enter username in the 'Settings' menu";
            }
        }

        private int checkExtension(String iFilePath)
        {
            if (iFilePath.Substring(iFilePath.Length - 3, 3) == "m3u")
            {
                return Global.PLAYLISTFORMATM3U;
            }
            if (iFilePath.Substring(iFilePath.Length - 4, 4).Equals("m3u8"))
            {
                return Global.PLAYLISTFORMATM3U8;
            }

            return Global.PLAYLISTFORMATDEFAULT;
        }

        private String getModeText(int mode, int subtotal)
        {
            String tempOutput = ("\nSubtotal: " + subtotal.ToString() + "\n\n");
            if (mode != Global.MNONE)
            {
                tempOutput = (tempOutput + "||| ");
                switch (mode)
                {
                    case Global.MLOVED:
                        tempOutput = (tempOutput + "Loved Tracks");
                        break;
                    case Global.MLOVEDA:
                        tempOutput = (tempOutput + "Loved Artists' Tracks");
                        break;
                    case Global.MREC:
                        tempOutput = (tempOutput + "Recommended Tracks");
                        break;
                    case Global.MLOADER:
                    default:
                        tempOutput = (tempOutput + "Unclassified");
                        break;
                }
                tempOutput = (tempOutput + " |||\n---\n");
            }
            return tempOutput;
        }

        private void enableComponents(Boolean enabled)
        {
            settingsToolStripMenuItem.Enabled = enabled;
            rtOutput.Enabled = enabled;
            chkLoved.Enabled = enabled;
            chkRecommended.Enabled = enabled;
            chkLovedArtists.Enabled = enabled;
        }

        private void chkLoved_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.chkLoved = chkLoved.Checked;
            if (chkLoved.Checked == false)
            {
                chkLovedArtists.Checked = false;
                chkLovedArtists.Enabled = false;
                Properties.Settings.Default.chkLovedA = chkLovedArtists.Checked;
                chkLovedT.Checked = false;
                chkLovedT.Enabled = false;
                Properties.Settings.Default.chkSimT = chkLovedT.Checked;
            }
            else
            {
                chkLovedArtists.Enabled = true;
                chkLovedT.Enabled = true;
            }
            Properties.Settings.Default.Save();
        }

        private void chkLovedArtists_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.chkLovedA = chkLovedArtists.Checked;
            Properties.Settings.Default.Save();
        }

        private void chkRecommended_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.chkRec = chkRecommended.Checked;
            Properties.Settings.Default.Save();
        }

        private void chkLovedT_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.chkSimT = chkLovedT.Checked;
            Properties.Settings.Default.Save();
        }
    }
}
